﻿using System;
using System.ComponentModel;
using System.Threading;
using System.Windows.Forms;

namespace GDFlacTool.Forms
{
    /// <summary>
    /// Simple progress form.
    /// </summary>
    public partial class FormProgressWorker : Form
    {
        private readonly BackgroundWorker _worker;
        private int _lastPercent;
        private string _lastStatus;
        //private bool _isFinished;

        /// <summary>
        /// Gets the progress bar so it is possible to customize it
        /// before displaying the form.
        /// Do not use it directly from the background worker function!
        /// </summary>
        public GDFlacTools.Controls.Progress.ProgressBar ProgressBar { get { return prbProcess; } }
        /// <summary>
        /// Will be passed to the background worker.
        /// </summary>
        public object Argument { get; set; }
        /// <summary>
        /// Background worker's result.
        /// You may also check ShowDialog return value
        /// to know how the background worker finished.
        /// </summary>
        public RunWorkerCompletedEventArgs Result { get; private set; }
        /// <summary>
        /// True if the user clicked the Cancel button
        /// and the background worker is still running.
        /// </summary>
        public bool CancellationPending
        {
            get { return _worker.CancellationPending; }
        }
        /// <summary>
        /// Text displayed once the Cancel button is clicked.
        /// </summary>
        public string CancellingText { get; set; }
        /// <summary>
        /// Default status text.
        /// </summary>
        public string DefaultStatusText { get; set; }

        //public bool IsBusy
        //{
        //    get { return _isFinished; }
        //}

        #region Delegates.

        /// <summary>
        /// Delegate for the DoWork event.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">Contains the event data.</param>
        public delegate void DoWorkEventHandler(FormProgressWorker sender, DoWorkEventArgs e);

        /// <summary>
        /// Occurs when the background worker starts.
        /// </summary>
        public event DoWorkEventHandler DoWorkGetTagInformation;

        /// <summary>
        /// Occurs when the background worker starts.
        /// </summary>
        public event DoWorkEventHandler DoWorkFillTracklist;

        /// <summary>
        /// Occurs when the background worker starts.
        /// </summary>
        public event DoWorkEventHandler DoWorkRenameFiles;

        /// <summary>
        /// Occurs when the background worker starts.
        /// </summary>
        public event DoWorkEventHandler DoWorkUndoRenameFile;

        /// <summary>
        /// Occurs when the background worker starts.
        /// </summary>
        public event DoWorkEventHandler DoWorkPasteTag;
            
        /// <summary>
        /// Occurs when the background worker starts.
        /// </summary>
        public event DoWorkEventHandler DoWorkAddFolderPictureToTag;

        /// <summary>
        /// Occurs when the background worker starts.
        /// </summary>
        public event DoWorkEventHandler DoWorkAddCoverArt;

        /// <summary>
        /// Occurs when the background worker starts.
        /// </summary>
        public event DoWorkEventHandler DoWorkRemoveCoverArt;

        /// <summary>
        /// Occurs when the background worker starts.
        /// </summary>
        public event DoWorkEventHandler DoWorkWriteTags;

        /// <summary>
        /// Occurs when the background worker starts.
        /// </summary>
        public event DoWorkEventHandler DoWorkRemoveTags;

        /// <summary>
        /// Occurs when the background worker starts.
        /// </summary>
        public event DoWorkEventHandler DoWorkSaveAllChanges;

        /// <summary>
        /// Occurs when the background worker starts.
        /// </summary>
        public event DoWorkEventHandler DoWorkAutoNumberTrack;

        /// <summary>
        /// Occurs when the background worker starts.
        /// </summary>
        public event DoWorkEventHandler DoWorkAutoNumberDisc;

        /// <summary>
        /// Occurs when the background worker starts.
        /// </summary>
        public event DoWorkEventHandler DoWorkGetTitleFromFile;

        /// <summary>
        /// Occurs when the background worker starts.
        /// </summary>
        public event DoWorkEventHandler DoWorkFormatTrackToX;

        /// <summary>
        /// Occurs when the background worker starts.
        /// </summary>
        public event DoWorkEventHandler DoWorkFormatTrackTo0X;

        /// <summary>
        /// Occurs when the background worker starts.
        /// </summary>
        public event DoWorkEventHandler DoWorkFormatTrackTo00X;

        /// <summary>
        /// Occurs when the background worker starts.
        /// </summary>
        public event DoWorkEventHandler DoWorkFormatTrackToXxxx;

        /// <summary>
        /// Occurs when the background worker starts.
        /// </summary>
        public event DoWorkEventHandler DoWorkFormatTrackToXx;

        /// <summary>
        /// Occurs when the background worker starts.
        /// </summary>
        public event DoWorkEventHandler DoWorkDeleteTrackNumber;

        /// <summary>
        /// Occurs when the background worker starts.
        /// </summary>
        public event DoWorkEventHandler DoWorkWritingTagsFromFileName;

        /// <summary>
        /// Occurs when the background worker starts.
        /// </summary>
        public event DoWorkEventHandler DoWorkCopyTagField;

        /// <summary>
        /// Occurs when the background worker starts.
        /// </summary>
        public event DoWorkEventHandler DoWorkSwapTagField;

        /// <summary>
        /// Occurs when the background worker starts.
        /// </summary>
        public event DoWorkEventHandler DoWorkTrimTrailingTagFields;

        /// <summary>
        /// Occurs when the background worker starts.
        /// </summary>
        public event DoWorkEventHandler DoWorkTrimLeadingTagFields;

        /// <summary>
        /// Occurs when the background worker starts.
        /// </summary>
        public event DoWorkEventHandler DoWorkSwapArtistAlbum;

        /// <summary>
        /// Occurs when the background worker starts.
        /// </summary>
        public event DoWorkEventHandler DoWorkSwapArtistTitle;

        /// <summary>
        /// Occurs when the background worker starts.
        /// </summary>
        public event DoWorkEventHandler DoWorkSwapAlbumTitle;

        /// <summary>
        /// Occurs when the background worker starts.
        /// </summary>
        public event DoWorkEventHandler DoWorkRemoveLeadingThe;

        /// <summary>
        /// Occurs when the background worker starts.
        /// </summary>
        public event DoWorkEventHandler DoWorkRemoveTrailingThe;

        /// <summary>
        /// Occurs when the background worker starts.
        /// </summary>
        public event DoWorkEventHandler DoWorkFormatArtistSurnameName;

        /// <summary>
        /// Occurs when the background worker starts.
        /// </summary>
        public event DoWorkEventHandler DoWorkFormatArtistNameSurname;

        /// <summary>
        /// Occurs when the background worker starts.
        /// </summary>
        public event DoWorkEventHandler DoWorkFindAndReplaceInTags;

        /// <summary>
        /// Occurs when the background worker starts.
        /// </summary>
        public event DoWorkEventHandler DoWorkConvertAudio;

        #endregion

        #region Constructor.

        /// <summary>
        /// Constructor.
        /// </summary>
        public FormProgressWorker()
        {
            InitializeComponent();

            DefaultStatusText = "Please wait...";
            CancellingText = "Cancelling operation...";

            _worker = new BackgroundWorker {WorkerReportsProgress = true, WorkerSupportsCancellation = true};
            
            _worker.DoWork += (worker_DoWorkGetTagInformation);
            _worker.DoWork += (worker_DoWorkFillTrackList);
            _worker.DoWork += (worker_DoWorkRenameFiles);
            _worker.DoWork += (worker_DoWorkUndoRenameFiles);
            _worker.DoWork += (worker_DoWorkPasteTag);
            _worker.DoWork += (worker_DoWorkAddFolderPictureToTag);
            _worker.DoWork += (worker_DoWorkAddCoverArt);
            _worker.DoWork += (worker_DoWorkRemoveCoverArt);
            _worker.DoWork += (worker_DoWorkWriteTags);
            _worker.DoWork += (worker_DoWorkRemoveTags);
            _worker.DoWork += (worker_DoWorkSaveAllChanges);
            _worker.DoWork += (worker_DoWorkAutoNumberTrack);
            _worker.DoWork += (worker_DoWorkAutoNumberDisc);
            _worker.DoWork += (worker_DoWorkGetTitleFromFile);
            _worker.DoWork += (worker_DoWorkFormatTrackToX);
            _worker.DoWork += (worker_DoWorkFormatTrackTo0X);
            _worker.DoWork += (worker_DoWorkFormatTrackTo00X);
            _worker.DoWork += (worker_DoWorkFormatTrackToXXXX);
            _worker.DoWork += (worker_DoWorkFormatTrackToXX);
            _worker.DoWork += (worker_DoWorkDeleteTrackNumber);
            _worker.DoWork += (worker_DoWorkWritingTagsFromFileName);
            _worker.DoWork += (worker_DoWorkCopyTagField);
            _worker.DoWork += (worker_DoWorkSwapTagField);
            _worker.DoWork += (worker_DoWorkTrimTrailingTagFields);
            _worker.DoWork += (worker_DoWorkTrimLeadingTagFields);
            _worker.DoWork += (worker_DoWorkSwapArtistAlbum);
            _worker.DoWork += (worker_DoWorkSwapArtistTitle);
            _worker.DoWork += (worker_DoWorkSwapAlbumTitle);
            _worker.DoWork += (worker_DoWorkRemoveLeadingThe);
            _worker.DoWork += (worker_DoWorkRemoveTrailingThe);
            _worker.DoWork += (worker_DoWorkFormatArtistSurnameName);
            _worker.DoWork += (worker_DoWorkFormatArtistNameSurname);
            _worker.DoWork += (worker_DoWorkFindAndReplaceInTags);
            _worker.DoWork += (worker_DoWorkConvertAudio);
            
            _worker.ProgressChanged += (worker_ProgressChanged);
            _worker.RunWorkerCompleted += (worker_RunWorkerCompleted);
        }

        #endregion

        public void worker_DoWorkGetTagInformation(object sender, DoWorkEventArgs e)
        {
            if (DoWorkGetTagInformation != null)
                DoWorkGetTagInformation(this, e);
        }

        public void worker_DoWorkFillTrackList(object sender, DoWorkEventArgs e)
        {
            if (DoWorkFillTracklist != null)
                DoWorkFillTracklist(this, e);
        }

        public void worker_DoWorkRenameFiles(object sender, DoWorkEventArgs e)
        {
            if (DoWorkRenameFiles != null)
                DoWorkRenameFiles(this, e);
        }

        public void worker_DoWorkUndoRenameFiles(object sender, DoWorkEventArgs e)
        {
            if (DoWorkUndoRenameFile != null)
                DoWorkUndoRenameFile(this, e);
        }

        public void worker_DoWorkPasteTag(object sender, DoWorkEventArgs e)
        {
            if (DoWorkPasteTag != null)
                DoWorkPasteTag(this, e);
        }

        public void worker_DoWorkAddFolderPictureToTag(object sender, DoWorkEventArgs e)
        {
            if (DoWorkAddFolderPictureToTag != null)
                DoWorkAddFolderPictureToTag(this, e);
        }

        public void worker_DoWorkAddCoverArt(object sender, DoWorkEventArgs e)
        {
            if (DoWorkAddCoverArt != null)
                DoWorkAddCoverArt(this, e);
        }

        public void worker_DoWorkRemoveCoverArt(object sender, DoWorkEventArgs e)
        {
            if (DoWorkRemoveCoverArt != null)
                DoWorkRemoveCoverArt(this, e);
        }

        public void worker_DoWorkWriteTags(object sender, DoWorkEventArgs e)
        {
            if (DoWorkWriteTags != null)
                DoWorkWriteTags(this, e);
        }

        public void worker_DoWorkRemoveTags(object sender, DoWorkEventArgs e)
        {
            if (DoWorkRemoveTags != null)
                DoWorkRemoveTags(this, e);
        }

        public void worker_DoWorkSaveAllChanges(object sender, DoWorkEventArgs e)
        {
            if (DoWorkSaveAllChanges != null)
                DoWorkSaveAllChanges(this, e);
        }

        public void worker_DoWorkAutoNumberTrack(object sender, DoWorkEventArgs e)
        {
            if (DoWorkAutoNumberTrack != null)
                DoWorkAutoNumberTrack(this, e);
        }

        public void worker_DoWorkAutoNumberDisc(object sender, DoWorkEventArgs e)
        {
            if (DoWorkAutoNumberDisc != null)
                DoWorkAutoNumberDisc(this, e);
        }

        public void worker_DoWorkGetTitleFromFile(object sender, DoWorkEventArgs e)
        {
            if (DoWorkGetTitleFromFile != null)
                DoWorkGetTitleFromFile(this, e);
        }

        public void worker_DoWorkFormatTrackToX(object sender, DoWorkEventArgs e)
        {
            if (DoWorkFormatTrackToX != null)
                DoWorkFormatTrackToX(this, e);
        }

        public void worker_DoWorkFormatTrackTo0X(object sender, DoWorkEventArgs e)
        {
            if (DoWorkFormatTrackTo0X != null)
                DoWorkFormatTrackTo0X(this, e);
        }

        public void worker_DoWorkFormatTrackTo00X(object sender, DoWorkEventArgs e)
        {
            if (DoWorkFormatTrackTo00X != null)
                DoWorkFormatTrackTo00X(this, e);
        }

        public void worker_DoWorkFormatTrackToXXXX(object sender, DoWorkEventArgs e)
        {
            if (DoWorkFormatTrackToXxxx != null)
                DoWorkFormatTrackToXxxx(this, e);
        }

        public void worker_DoWorkFormatTrackToXX(object sender, DoWorkEventArgs e)
        {
            if (DoWorkFormatTrackToXx != null)
                DoWorkFormatTrackToXx(this, e);
        }

        public void worker_DoWorkDeleteTrackNumber(object sender, DoWorkEventArgs e)
        {
            if (DoWorkDeleteTrackNumber != null)
                DoWorkDeleteTrackNumber(this, e);
        }

        public void worker_DoWorkWritingTagsFromFileName(object sender, DoWorkEventArgs e)
        {
            if (DoWorkWritingTagsFromFileName != null)
                DoWorkWritingTagsFromFileName(this, e);
        }

        public void worker_DoWorkCopyTagField(object sender, DoWorkEventArgs e)
        {
            if (DoWorkCopyTagField != null)
                DoWorkCopyTagField(this, e);
        }

        public void worker_DoWorkSwapTagField(object sender, DoWorkEventArgs e)
        {
            if (DoWorkSwapTagField != null)
                DoWorkSwapTagField(this, e);
        }

        public void worker_DoWorkTrimTrailingTagFields(object sender, DoWorkEventArgs e)
        {
            if (DoWorkTrimTrailingTagFields != null)
                DoWorkTrimTrailingTagFields(this, e);
        }

        public void worker_DoWorkTrimLeadingTagFields(object sender, DoWorkEventArgs e)
        {
            if (DoWorkTrimLeadingTagFields != null)
                DoWorkTrimLeadingTagFields(this, e);
        }

        public void worker_DoWorkSwapArtistAlbum(object sender, DoWorkEventArgs e)
        {
            if (DoWorkSwapArtistAlbum != null)
                DoWorkSwapArtistAlbum(this, e);
        }

        public void worker_DoWorkSwapArtistTitle(object sender, DoWorkEventArgs e)
        {
            if (DoWorkSwapArtistTitle != null)
                DoWorkSwapArtistTitle(this, e);
        }

        public void worker_DoWorkSwapAlbumTitle(object sender, DoWorkEventArgs e)
        {
            if (DoWorkSwapAlbumTitle != null)
                DoWorkSwapAlbumTitle(this, e);
        }

        public void worker_DoWorkRemoveLeadingThe(object sender, DoWorkEventArgs e)
        {
            if (DoWorkRemoveLeadingThe != null)
                DoWorkRemoveLeadingThe(this, e);
        }

        public void worker_DoWorkRemoveTrailingThe(object sender, DoWorkEventArgs e)
        {
            if (DoWorkRemoveTrailingThe != null)
                DoWorkRemoveTrailingThe(this, e);
        }

        public void worker_DoWorkFormatArtistSurnameName(object sender, DoWorkEventArgs e)
        {
            if (DoWorkFormatArtistSurnameName != null)
                DoWorkFormatArtistSurnameName(this, e);
        }

        public void worker_DoWorkFormatArtistNameSurname(object sender, DoWorkEventArgs e)
        {
            if (DoWorkFormatArtistNameSurname != null)
                DoWorkFormatArtistNameSurname(this, e);
        }

        public void worker_DoWorkFindAndReplaceInTags(object sender, DoWorkEventArgs e)
        {
            if (DoWorkFindAndReplaceInTags != null)
                DoWorkFindAndReplaceInTags(this, e);
        }

        public void worker_DoWorkConvertAudio(object sender, DoWorkEventArgs e)
        {
            if (DoWorkConvertAudio != null)
                DoWorkConvertAudio(this, e);
        }

        /// <summary>
        /// Cancelling the operation
        /// </summary>
        public void CancelOperation()
        {
            if (!_worker.CancellationPending)
            {
                _worker.CancelAsync();
                btnCancel.Enabled = false;
                lblStatus.Text = CancellingText;
            }
        }

        /// <summary>
        /// Changes the status text only.
        /// </summary>
        /// <param name="status">New status text.</param>
        /// <param name="sleep">Pause in milliseconds</param>
        public void SetProgress(string status, int sleep)
        {
            //do not update the text if it didn't change
            //or if a cancellation request is pending
            if (status != _lastStatus && !_worker.CancellationPending)
            {
                Thread.Sleep(sleep);
                _lastStatus = status;
                _worker.ReportProgress(prbProcess.Minimum - 1, status);
            }
        }
        /// <summary>
        /// Changes the progress bar value only.
        /// </summary>
        /// <param name="percent">New value for the progress bar.</param>
        /// <param name="sleep">Pause in milliseconds</param>
        public void SetProgress(int percent, int sleep)
        {
            //do not update the progress bar if the value didn't change
            if (percent != _lastPercent)
            {
                Thread.Sleep(sleep);
                _lastPercent = percent;
                _worker.ReportProgress(percent);
            }
        }
        /// <summary>
        /// Changes both progress bar value and status text.
        /// </summary>
        /// <param name="percent">New value for the progress bar.</param>
        /// <param name="status">New status text.</param>
        /// <param name="sleep">Pause in milliseconds</param>
        public void SetProgress(int percent, string status, int sleep)
        {
            //update the form is at least one of the values need to be updated
            if (percent != _lastPercent || (status != _lastStatus && !_worker.CancellationPending))
            {
                Thread.Sleep(sleep);
                _lastPercent = percent;
                _lastStatus = status;
                _worker.ReportProgress(percent, status);
            }
            else if(percent == 100)
            {
                
                _worker.ReportProgress(100, status);
            }
        }

        private void ProgressForm_Load(object sender, EventArgs e)
        {
            //reset to defaults just in case the user wants to reuse the form
            Result = null;
            btnCancel.Enabled = true;
            prbProcess.Value = prbProcess.Minimum;
            lblStatus.Text = DefaultStatusText;
            _lastStatus = DefaultStatusText;
            _lastPercent = prbProcess.Minimum;
            //start the background worker as soon as the form is loaded
            _worker.RunWorkerAsync(Argument);
        }

        private void buttonCancel_Click(object sender, EventArgs e)
        {
            //notify the background worker we want to cancel
            _worker.CancelAsync();
            //disable the cancel button and change the status text
            btnCancel.Enabled = false;
            lblStatus.Text = CancellingText;
        }

        void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            //make sure the new value is valid for the progress bar and update it
            if (e.ProgressPercentage >= prbProcess.Minimum && e.ProgressPercentage <= prbProcess.Maximum)
                prbProcess.Value = e.ProgressPercentage;
            //do not update the text if a cancellation request is pending
            if (e.UserState != null && !_worker.CancellationPending)
                lblStatus.Text = e.UserState.ToString();
        }

        void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            //the background worker completed
            //keep the resul and close the form
            //Thread.Sleep(1000);
            Result = e;
            if (e.Error != null)
                DialogResult = DialogResult.Abort;
            else if (e.Cancelled)
                DialogResult = DialogResult.Cancel;
            else
                DialogResult = DialogResult.OK;
            Close();
        }
    }
}
